﻿
namespace Aeon.Emulator.Instructions.BitShifting
{
    internal static class Shld
    {
        [Opcode("0FA4/r rmw,rw,ib|0FA5/r rmw,rw,cl", AddressSize = 16 | 32)]
        public static void Shld16(Processor p, ref ushort dest, ushort src, byte count)
        {
            int actualCount = count % 32;

            if(actualCount == 0)
                return;

            int result = (dest << actualCount) | (src >> (16 - actualCount));
            if((result & 0x00010000) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if(count == 1)
            {
                if(((result ^ dest) & 0x8000) == 0x8000)
                    p.Flags |= EFlags.Overflow;
                else
                    p.Flags &= ~EFlags.Overflow;
            }

            p.WordUpdateFlags((short)result, (uint)result, EFlags.Sign | EFlags.Zero | EFlags.Parity);
            dest = (ushort)(uint)result;
        }
        [Alternate("Shld16", AddressSize = 16 | 32)]
        public static void Shld32(Processor p, ref uint dest, uint src, byte count)
        {
            int actualCount = count % 32;

            if(actualCount == 0)
                return;

            uint result = (dest << actualCount) | (src >> (32 - actualCount));
            if((dest & (1 << (actualCount - 1))) != 0)
                p.Flags |= EFlags.Carry;
            else
                p.Flags &= ~EFlags.Carry;

            if(count == 1)
            {
                if(((result ^ dest) & 0x80000000) == 0x80000000)
                    p.Flags |= EFlags.Overflow;
                else
                    p.Flags &= ~EFlags.Overflow;
            }

            p.DWordUpdateFlags((int)result, result, EFlags.Sign | EFlags.Zero | EFlags.Parity);
            dest = result;
        }
    }

    internal static class Shrd
    {
        [Opcode("0FAC/r rmw,rw,ib|0FAD/r rmw,rw,cl", AddressSize = 16 | 32)]
        public static void Shrd16(Processor p, ref ushort dest, ushort src, byte count)
        {
            int actualCount = count % 32;
            
            if(actualCount == 0)
                return;

            int result;
            if(actualCount == 1)
            {
                if((dest & 1) != 0)
                    p.Flags |= EFlags.Carry;
                else
                    p.Flags &= ~EFlags.Carry;

                result = (dest >> 1) | (src << 15);

                if(((result ^ dest) & 0x8000) == 0x8000)
                    p.Flags |= EFlags.Overflow;
                else
                    p.Flags &= ~EFlags.Overflow;
            }
            else
            {
                result = (dest >> (actualCount - 1)) | (src << (16 - (actualCount - 1)));
                if((dest & 1) != 0)
                    p.Flags |= EFlags.Carry;
                else
                    p.Flags &= ~EFlags.Carry;

                result >>= 1;
            }

            dest = (ushort)result;
        }

        [Alternate("Shrd16", AddressSize = 16 | 32)]
        public static void Shrd32(Processor p, ref uint dest, uint src, byte count)
        {
            int actualCount = count % 32;

            if(actualCount == 0)
                return;

            long result;
            if(actualCount == 1)
            {
                if((dest & 1) != 0)
                    p.Flags |= EFlags.Carry;
                else
                    p.Flags &= ~EFlags.Carry;

                result = ((long)dest >> 1) | ((long)src << 31);

                if(((result ^ dest) & 0x80000000) == 0x80000000)
                    p.Flags |= EFlags.Overflow;
                else
                    p.Flags &= ~EFlags.Overflow;
            }
            else
            {
                result = ((long)dest >> (actualCount - 1)) | ((long)src << (32 - (actualCount - 1)));
                if((dest & 1) != 0)
                    p.Flags |= EFlags.Carry;
                else
                    p.Flags &= ~EFlags.Carry;

                result >>= 1;
            }

            dest = (uint)result;
        }
    }
}
